package spaces;

import com.appclient.DaoInstanceEnum;
import com.carpark.ejb.DAO.DAO;
import com.carpark.ejb.Model.ParkingpassesModel;
import com.carpark.ejb.Model.ParkingspacesModel;
import com.configurator.ConfiguratorPanelController;
import com.configurator.FieldData;
import com.configurator.SpaceButton;
import configurationReader.SpacesReader;
import exceptions.LevelExistException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Side;
import javafx.scene.control.Control;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.GridPane;

/**
 *
 * @author Sawior
 */
public class Spaces {

    private static final String SPACE_DEFAULT_STYLE = "space-creator-new";
    private static final String SPACE_FREE_STYLE = "space-creator-space";
    private static final String SPACE_DISABLED_STYLE = "space-creator-space-disable";
    private static final String SPACE_OCCUPIED_STYLE = "space-creator-space-occupied";
    private static final String SPACE_ROAD_STYLE = "space-creator-road";
    private static final String TAB_STYLE = "tabPane-spaces";
    public static final String ENTRY = "Entry";
    public static final String EXIT = "Exit";
    private static final int PREF_GRID_WIDTH = 1200;
    private static final String TAB_SPACES_STYLE = "tabPane-spaces";
    private List<FieldData> spacesLikeFields;
    private HashMap<Integer, Tab> tabs = new HashMap<>();
    private TabPane levelTabs = new TabPane();
    private EventHandler<ActionEvent> eventController;
    private HashMap<Integer, SpaceButton> buttons = new HashMap<>();
    private String spacesConfigType;
    private boolean isNewClientMode = false;
    private HashMap<Integer, ParkingspacesModel> spacesForOtherOp;
    private Date startDate;
    private Date endDate;
    private String carType;

    public Spaces(EventHandler<ActionEvent> eventController, String type, HashMap<Integer, ParkingspacesModel> spacesForOtherOp, Date startDate, Date endDate, String carType) {
        spacesLikeFields = new ArrayList(SpacesReader.loadSpacesFile().values());
        this.startDate = startDate;
        this.endDate = endDate;
        this.carType = carType;
        checkFileDataWithDataBase(spacesForOtherOp);
        this.eventController = eventController;
        levelTabs.setSide(Side.LEFT);
        levelTabs.getStyleClass().add(TAB_STYLE);
        levelTabs.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
        spacesConfigType = type;
    }

    public void checkFileDataWithDataBase(HashMap<Integer, ParkingspacesModel> spacesForOtherOp) {
        DAO dao = DaoInstanceEnum.INSTANCE.getDao();
        if (spacesForOtherOp != null) {
            this.spacesForOtherOp = spacesForOtherOp;
            isNewClientMode = true;
        }
        for (FieldData fieldData : spacesLikeFields) {
            ParkingspacesModel dbRecord = null;
            try {
                if (isNewClientMode) {
                    dbRecord = spacesForOtherOp.get(fieldData.getSpaceNumber());
                } else {
                    dbRecord = dao.getParkingSpacesByNumber(fieldData.getSpaceNumber());
                }
            } catch (Throwable t) {
                Logger.getLogger(Spaces.class.getName()).log(Level.SEVERE, "[CarPark] Database record cannot be loaded.");
            }
            if (isNewClientMode) {
                if (dbRecord != null) {
                    fieldData.setSpaceStatus("Free");
                    fieldData.setSpaceType(dbRecord.getType());
                } else {
                    fieldData.setSpaceStatus("Disabled");
                }
            } else {
                if (dbRecord != null) {
                    fieldData.setSpaceStatus(dbRecord.getStatus());
                    fieldData.setSpaceType(dbRecord.getType());
                }
            }
        }
    }

    public void makeRefresh() {
        DAO dao = DaoInstanceEnum.INSTANCE.getDao();
        List<ParkingspacesModel> dbSpaces = dao.getAllParkingSpaces();
        if (isNewClientMode) {
            dbSpaces = dao.getParkingSpacesForReservation(this.startDate, this.endDate, this.carType);
        }
        for (ParkingspacesModel dbSpace : dbSpaces) {
            if (isNewClientMode) {
                entryStyle(new FieldData("Stanowisko", dbSpace.getNumber(), dbSpace.getType(), "Free", 0, 0, 0), buttons.get(dbSpace.getNumber()));
            } else {
                if (this.spacesConfigType.equals(Spaces.ENTRY)) {
                    entryStyle(new FieldData("Stanowisko", dbSpace.getNumber(), dbSpace.getType(), dbSpace.getStatus(), 0, 0, 0), buttons.get(dbSpace.getNumber()));
                } else {
                    exitStyle(new FieldData("Stanowisko", dbSpace.getNumber(), dbSpace.getType(), dbSpace.getStatus(), 0, 0, 0), buttons.get(dbSpace.getNumber()));
                }
            }
        }
    }

    public TabPane getSpacesForEntries() {
        List<Integer> levels = getLevels(spacesLikeFields);
        for (Integer level : levels) {
            Integer[] maxs = getDimmensions(level);
            Integer maxColumn = maxs[0] + 1;
            Integer maxRow = maxs[1] + 1;
            List<FieldData> fields = getFieldsForLevel(level);
            createNewTab(level, maxColumn, maxRow, fields);
        }
        TimerTask task = new SpacesTimeScheduler(this);
        Timer timer = new Timer(true);
        timer.schedule(task, 1000, 20000);
   
        return levelTabs;
    }

    private List<Integer> getLevels(List<FieldData> fields) {
        List<Integer> levels = new ArrayList<>();

        for (FieldData current : fields) {
            Integer level = current.getLevel();
            if (!levels.contains(level)) {
                levels.add(level);
            }
        }
        return levels;
    }

    private Integer[] getDimmensions(Integer level) {
        int maxColumn = 0;
        int maxRow = 0;
        for (FieldData field : spacesLikeFields) {
            if (field.getLevel() == level) {
                if (field.getX() > maxColumn) {
                    maxColumn = field.getX();
                }
                if (field.getY() > maxRow) {
                    maxRow = field.getY();
                }
            }
        }

        return new Integer[]{maxColumn, maxRow};
    }

    private List<FieldData> getFieldsForLevel(Integer level) {
        List<FieldData> data = new ArrayList<>();
        for (FieldData field : spacesLikeFields) {
            if (field.getLevel() == level) {
                data.add(field);
            }
        }

        return data;
    }

    private Tab createNewTab(Integer id, Integer x, Integer y, List<FieldData> data) {
        Tab createdTab = new Tab();
        try {
            createNewTabFxmlSettings(createdTab, id);
        } catch (LevelExistException ex) {
            Logger.getLogger(Spaces.class.getName()).log(Level.SEVERE, "[CarPark] This spaces level already exists.");
            return null;
        }
        createdTab.setContent(createGridPane(y, x, id, data));
        addTabToSortedPlace();
        return createdTab;
    }

    private ScrollPane createGridPane(int rows, int columns, Integer level, List<FieldData> data) {
        ScrollPane scroll = new ScrollPane();
        GridPane grid = createDefaultGridPaneSettings();
        for (int i = 0; i < columns; i++) {
            for (int j = 0; j < rows; j++) {
                final SpaceButton currrentSpaceButton = new SpaceButton(i, j, level);
                createDefaultButtonSettings(currrentSpaceButton);
                FieldData currentSpaceButtonData = getDataForCurrentSpaceButton(i, j, data);
                fetchCorrectStyleToButton(currrentSpaceButton, currentSpaceButtonData);
                grid.add(currrentSpaceButton, i, j);
            }
        }
        scroll.setContent(grid);
        scroll.getStyleClass().add(TAB_SPACES_STYLE);

        return scroll;
    }

    private void createDefaultButtonSettings(final SpaceButton oneSpace) {
        oneSpace.setOnAction(eventController);
        oneSpace.setMinWidth(30);
        oneSpace.setMinHeight(30);
        oneSpace.setPrefWidth(70);
        oneSpace.setPrefHeight(70);
        oneSpace.getStyleClass().add(SPACE_DEFAULT_STYLE);
    }

    private GridPane createDefaultGridPaneSettings() {
        GridPane grid = new GridPane();
        grid.setMinWidth(Control.USE_COMPUTED_SIZE);
        grid.setPrefWidth(PREF_GRID_WIDTH);
        grid.paddingProperty().setValue(new Insets(20));
        return grid;
    }

    private void createNewTabFxmlSettings(Tab createdTab, Integer id) throws LevelExistException {
        createdTab.setId(id.toString());
        createdTab.setText("Poziom " + id.toString());
        createdTab.getStyleClass().add("tabPane-spaces");
        if (tabs.containsKey(id)) {
            throw new LevelExistException();
        }
        tabs.put(id, createdTab);
    }

    private void addTabToSortedPlace() {
        List<Map.Entry<Integer, Tab>> entries = new ArrayList<>(tabs.entrySet());
        Collections.sort(entries, new Comparator<Map.Entry<Integer, Tab>>() {
            @Override
            public int compare(Map.Entry<Integer, Tab> a, Map.Entry<Integer, Tab> b) {
                if (a.getKey() == b.getKey()) {
                    return 0;
                }
                if (a.getKey() > b.getKey()) {
                    return -1;
                }
                return 1;
            }
        });
        levelTabs.getTabs().clear();
        for (Map.Entry<Integer, Tab> entry : entries) {
            levelTabs.getTabs().add(entry.getValue());
        }

    }

    private void fetchCorrectStyleToButton(SpaceButton currentButtonSpace, FieldData currentSpaceButtonData) {
        if (currentSpaceButtonData == null) {
            currentButtonSpace.getStyleClass().add(SPACE_ROAD_STYLE);
            return;
        }
        String plate = "";
        try {
            ParkingspacesModel space = DaoInstanceEnum.INSTANCE.getDao().getParkingSpacesByNumber(currentSpaceButtonData.getSpaceNumber());
            List<ParkingpassesModel> passes = DaoInstanceEnum.INSTANCE.getDao().getParkingPassBySpaceId(space);
            ParkingpassesModel pass = passes.get(passes.size()-1);
            plate = pass.getCarId().getNumberPlate();
            plate = plate.replace("-", " - ");
        } catch (Throwable t) {
            //Logger.getLogger(Spaces.class.getName()).log(Level.SEVERE, "[CarPark] Cannot get plate number for current space.");
        }
        currentButtonSpace.setText(currentSpaceButtonData.getSpaceNumber() + " [" + convertSpaceTypeToChar(currentSpaceButtonData.getSpaceType()) + "] " + plate);
        currentButtonSpace.setId(currentSpaceButtonData.getSpaceNumber().toString());
        buttons.put(currentSpaceButtonData.getSpaceNumber(), currentButtonSpace);
        if (this.spacesConfigType.equals(ENTRY)) {
            entryStyle(currentSpaceButtonData, currentButtonSpace);
        } else if (this.spacesConfigType.equals(EXIT)) {
            exitStyle(currentSpaceButtonData, currentButtonSpace);
        }
    }

    private String convertSpaceTypeToChar(String spaceType) {
        if (spaceType.equals("Osobowe") || spaceType.equals("Personal")) {
            return "O";
        }
        if (spaceType.equals("Dostawcze") || spaceType.equals("Van")) {
            return "D";
        }
        if (spaceType.equals("Motor") || spaceType.equals("Motorcycle")) {
            return "M";
        }
        if (spaceType.equals("Przyczepka") || spaceType.equals("Trailer")) {
            return "P";
        }
        if (spaceType.equals("Specialne") || spaceType.equals("Special")) {
            return "S";
        }

        return "A";
    }

    private FieldData getDataForCurrentSpaceButton(int i, int j, List<FieldData> data) {
        for (FieldData fieldData : data) {
            if (fieldData.getX() == i && fieldData.getY() == j) {
                return fieldData;
            }
        }
        return null;
    }

    private void entryStyle(final FieldData currentSpaceButtonData, final SpaceButton currentButtonSpace) {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                if (currentSpaceButtonData.getSpaceStatus().equals("Free")) {
                    if (currentButtonSpace.getStyleClass().remove(SPACE_DEFAULT_STYLE)
                            || currentButtonSpace.getStyleClass().remove(SPACE_DISABLED_STYLE)
                            || currentButtonSpace.getStyleClass().remove(SPACE_OCCUPIED_STYLE)) {
                        currentButtonSpace.getStyleClass().add(SPACE_FREE_STYLE);
                    }
                }
                if (currentSpaceButtonData.getSpaceStatus().equals("Occupied")) {
                    if (currentButtonSpace.getStyleClass().remove(SPACE_DEFAULT_STYLE)
                            || currentButtonSpace.getStyleClass().remove(SPACE_DISABLED_STYLE)
                            || currentButtonSpace.getStyleClass().remove(SPACE_FREE_STYLE)) {
                        currentButtonSpace.getStyleClass().add(SPACE_OCCUPIED_STYLE);
                    }
                }
                if (currentSpaceButtonData.getSpaceStatus().equals("Disabled")) {
                    if (currentButtonSpace.getStyleClass().remove(SPACE_DEFAULT_STYLE)
                            || currentButtonSpace.getStyleClass().remove(SPACE_FREE_STYLE)
                            || currentButtonSpace.getStyleClass().remove(SPACE_OCCUPIED_STYLE)) {
                        currentButtonSpace.getStyleClass().add(SPACE_DISABLED_STYLE);
                    }
                }
            }
        });
    }

    private void exitStyle(final FieldData currentSpaceButtonData, final SpaceButton currentButtonSpace) {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                if (currentSpaceButtonData.getSpaceStatus().equals("Occupied")) {
                    if (currentButtonSpace.getStyleClass().remove(SPACE_DEFAULT_STYLE)
                            || currentButtonSpace.getStyleClass().remove(SPACE_DISABLED_STYLE)
                            || currentButtonSpace.getStyleClass().remove(SPACE_OCCUPIED_STYLE)) {
                        currentButtonSpace.getStyleClass().add(SPACE_FREE_STYLE);
                    }
                }
                if (currentSpaceButtonData.getSpaceStatus().equals("Free")) {
                    if (currentButtonSpace.getStyleClass().remove(SPACE_DEFAULT_STYLE)
                            || currentButtonSpace.getStyleClass().remove(SPACE_DISABLED_STYLE)
                            || currentButtonSpace.getStyleClass().remove(SPACE_FREE_STYLE)) {
                        currentButtonSpace.getStyleClass().add(SPACE_OCCUPIED_STYLE);
                    }
                }
                if (currentSpaceButtonData.getSpaceStatus().equals("Disabled")) {
                    if (currentButtonSpace.getStyleClass().remove(SPACE_DEFAULT_STYLE)
                            || currentButtonSpace.getStyleClass().remove(SPACE_FREE_STYLE)
                            || currentButtonSpace.getStyleClass().remove(SPACE_OCCUPIED_STYLE)) {
                        currentButtonSpace.getStyleClass().add(SPACE_DISABLED_STYLE);
                    }
                }
            }
        });
    }
    
}
